﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using Autofac;
using Autofac.Integration.WebApi;
using Newtonsoft.Json.Linq;
using Pay.Core;
using Pay.DTO;
using Pay.IService;
using Pay.TO;

namespace Pay.Api {
	public class MessageDelegatingHandler : DelegatingHandler {
		protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
			try {
				HttpConfiguration configuration = request.GetConfiguration();
				HttpControllerDescriptor controllerDescriptor =
					configuration.Services.GetHttpControllerSelector().SelectController(request);
				HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(),
														request.GetRouteData(), request) {
					ControllerDescriptor = controllerDescriptor
				};
				HttpActionDescriptor actionDescriptor =
					configuration.Services.GetActionSelector().SelectAction(controllerContext);
				var anonymousAction = actionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
				if (!anonymousAction.Any()) {
					var httpRequest = ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request;
					Stream stream = httpRequest.InputStream;
					StreamReader streamReader = new StreamReader(stream);
					var postData = streamReader.ReadToEnd();
					var reqt = JObject.Parse(postData);

#if !DEBUG
					var requestScope = request.GetDependencyScope();
					var requestLifetimeScope = requestScope.GetRequestLifetimeScope();
					var iMerchantService = requestLifetimeScope.Resolve<IMerchantService>();
					var merchant = iMerchantService.Get(reqt["merchant_no"].ToString());
					if (!RSAFromPkcs.verify(ApiSign.sign(reqt), reqt["sign"].ToString(), merchant.public_key, "utf-8"))
						return await requestCancel(request, cancellationToken, "签名不正确");
#endif
				}

				HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

				if (response.StatusCode != HttpStatusCode.OK) {
					int errorCode = 0;
					string message = LogError(request, response, out errorCode);
					if (string.IsNullOrEmpty(message))
						response = new HttpResponseMessage() {
							Content = new StringContent(ApiResultToJson.Json(new ApiResultMessage((int)response.StatusCode, GetMessage(response.StatusCode))).ToJson(), Encoding.UTF8, "application/json")
						};
					else
						response = new HttpResponseMessage() {
							Content = new StringContent(ApiResultToJson.Json(new ApiResultMessage(errorCode, message)).ToJson(), Encoding.UTF8, "application/json")
						};
				}

				return response;
			} catch {
				return await requestCancel(request, cancellationToken, "无权限访问");
			}
		}

		private Task<HttpResponseMessage> requestCancel(HttpRequestMessage request,
				CancellationToken cancellationToken, string message) {
			CancellationTokenSource _tokenSource = new CancellationTokenSource();
			cancellationToken = _tokenSource.Token;
			_tokenSource.Cancel();
			HttpResponseMessage response = new HttpResponseMessage();
			//response = request.CreateResponse(HttpStatusCode.Unauthorized);ios和安卓接收数据只能用异常来处理，所以不要，就是200成功的
			response.Content = new StringContent(ApiResultToJson.Json(new ApiResultMessage(401, message)).ToJson(), Encoding.UTF8, "application/json");
			return base.SendAsync(request, cancellationToken).ContinueWith(task => {
				return response;
			});
		}

		private string GetMessage(HttpStatusCode httpStatusCode) {
			string message = string.Empty;
			switch (httpStatusCode) {
				case HttpStatusCode.MethodNotAllowed:
					message = "指示请求的资源上不允许请求方法";
					break;
				case HttpStatusCode.NotFound:
					message = "指示请求的资源不在服务器上";
					break;
				case HttpStatusCode.Unauthorized:
					message = "指示请求的资源要求身份验证";
					break;
				case HttpStatusCode.Forbidden:
					message = "指示服务器拒绝满足请求";
					break;
				case HttpStatusCode.NotAcceptable:
					message = "指示客户端已用 Accept 头指示将不接受资源的任何可用表示形式";
					break;
				case HttpStatusCode.PreconditionFailed:
					message = "指示为此请求设置的条件失败，且无法执行此请求。条件是用条件请求标头（如 If-Match、If-None-Match 或 If-Unmodified-Since）设置的";
					break;
				case HttpStatusCode.NotImplemented:
					message = "指示服务器不支持请求的函数";
					break;
				case HttpStatusCode.BadGateway:
					message = "指示中间代理服务器从另一代理或原始服务器接收到错误响应";
					break;
				case HttpStatusCode.BadRequest:
					message = "指示服务器未能识别请求";
					break;
				default:
					message = "交易异常";
					break;
			}
			return message;
		}

		private string LogError(HttpRequestMessage request, HttpResponseMessage response, out int errorCode) {
			string message = string.Empty;
			errorCode = 0;
			if (response.Content != null) {
				var ex = response.Content.ReadAsAsync<HttpError>().Result;
				if (ex.ExceptionType == "Pay.Core.ServerErrorException" ||
					ex.ExceptionType == "Pay.Core.DataErrorException") {
					message = ex.ExceptionMessage;
					if (message.Contains(":")) {
						var arr = message.Split(':');
						message = arr[0];
						errorCode = int.Parse(arr[1]);
					}
				} else {
					if (!string.IsNullOrEmpty(ex.ExceptionMessage))
						message = ex.ExceptionMessage;
					else
						message = ex.Message;
					var requestScope = request.GetDependencyScope();
					var requestLifetimeScope = requestScope.GetRequestLifetimeScope();
					StringBuilder sb = new StringBuilder();
					sb.AppendFormat("Message:{0}\r\n", ex.Message);
					sb.AppendFormat("ExceptionMessage:{0}\r\n", ex.ExceptionMessage);
					sb.AppendFormat("ExceptionType:{0}\r\n", ex.ExceptionType);
					sb.AppendFormat("MessageDetail:{0}\r\n", ex.MessageDetail);
					sb.AppendFormat("StackTrace:{0}\r\n", ex.StackTrace);
					sb.AppendFormat("Url:{0}\r\n", response.RequestMessage.RequestUri);
					sb.AppendFormat("Method:{0}\r\n", response.RequestMessage.Method);
					sb.AppendFormat("ContentType:{0}\r\n", response.Content.Headers.ContentType);
					//sb.AppendFormat("RequestQuery:{0}\r\n", authHelper.query_str);
					//sb.AppendFormat("RequestForm:{0}\r\n", authHelper.form_str);
					//sb.AppendFormat("RequestOther:{0}\r\n", authHelper.other_str);

					var logHelper = requestLifetimeScope.ResolveNamed<ILogHelper>("ui_error");
					logHelper.Error(sb.ToString());
					message = string.Empty;
				}
			}
			return message;
		}
	}
}
